Last execution time: 27/03/2025 05:40:13
Get data
Products type filter
explore_types = ['frutas', 'lacteos', 'verduras', 'embutidos', 'panaderia', 'desayuno', 'congelados', 'abarrotes',
'aves', 'carnes', 'pescados']Data table
path = Path('../../output')
csv_files = L(path.glob('*.csv')).filter(lambda o: os.stat(o).st_size>0)
pat_store = re.compile('(.+)\_\d+')
pat_date = re.compile('.+\_(\d+)')
df = (
pd.concat([pd.read_csv(o).assign(store=pat_store.match(o.stem)[1], date=pat_date.match(o.stem)[1])
for o in csv_files], ignore_index=True)
.pipe(lambda d: d.assign(
name=d.name.str.lower()+' ('+d.store+')',
sku=d.id.where(d.sku.isna(), d.sku).astype(int),
date=pd.to_datetime(d.date)
))
.drop('id', axis=1)
.loc[lambda d: d.category.str.contains('|'.join(explore_types))]
# Filter products with recent data
# .loc[lambda d: d.name.isin(d.groupby('name').date.max().loc[ge(datetime.now()-timedelta(days=30))].index)]
# Filter empty prices
.loc[lambda d: d.price>0]
)
print(df.shape)
df.sample(3)(1316794, 8)
| sku | name | brand | category | uri | price | store | date | |
|---|---|---|---|---|---|---|---|---|
| 442377 | 11565328 | pimienta negra maras gourmet bolsa 30g (plaza_... | MARAS GOURMET | https://www.plazavea.com.pe/abarrotes | https://www.plazavea.com.pe/pimienta-negra-mar... | 5.40 | plaza_vea | 2025-01-30 |
| 691436 | 719 | aceitunas verdes deshuesadas en conserva olivo... | OLIVOS DEL SUR | https://www.plazavea.com.pe/abarrotes | https://www.plazavea.com.pe/aceitunas-verdes-d... | 13.50 | plaza_vea | 2023-10-30 |
| 1079576 | 41868 | mix galletas de salvado con orégano + galletas... | Nancy Natur | https://www.metro.pe/panaderia-y-pasteleria/la... | https://www.metro.pe/mix-galletas-de-salvado-c... | 14.49 | metro | 2023-06-29 |
Top changes (ratio)
Code
top_changes = (df
# Use last 30 days of data to compare prices
.loc[lambda d: d.date>=(datetime.now()-timedelta(days=30))]
.sort_values('date')
# Get percentage change
.assign(change=lambda d: d
.groupby(['store','sku'], as_index=False)
.price.transform(lambda d: (d-d.shift())/d.shift())
)
.groupby(['store','sku'], as_index=False)
.agg({'price':'last', 'change':'mean', 'date':'last'})
.rename({'price':'last_price', 'date':'last_date'}, axis=1)
.dropna()
.loc[lambda d: d.last_date==d.last_date.max()]
.loc[lambda d: d.change.abs().sort_values(ascending=False).index]
)
top_changes.head(3)| store | sku | last_price | change | last_date | |
|---|---|---|---|---|---|
| 538 | plaza_vea | 2574 | 7.9 | 1.317465 | 2025-03-27 |
| 1005 | plaza_vea | 4918 | 4.4 | 0.181902 | 2025-03-27 |
| 4142 | plaza_vea | 11069396 | 10.9 | 0.143545 | 2025-03-27 |
Code
def plot_changes(df_changes, title):
selection = alt.selection_point(fields=['name'], bind='legend')
dff = df_changes.drop('change', axis=1).merge(df, on=['store','sku'])
return (dff
.pipe(alt.Chart)
.mark_line(point=True)
.encode(
x='date',
y='price',
color=alt.Color('name').scale(domain=sorted(dff.name.unique().tolist())),
tooltip=['name','price','last_price']
)
.add_params(selection)
.transform_filter(selection)
.interactive()
.properties(width=650, title=title)
.configure_legend(orient='top', columns=3)
)Code
top_changes.head(10).pipe(plot_changes, 'Top changes')Code
(top_changes
.sort_values('change')
.head(10)
.pipe(plot_changes, 'Top drops')
)Code
(top_changes
.sort_values('change')
.tail(10)
.pipe(plot_changes, 'Top increases')
)Top changes (absolute values)
Code
top_changes_abs = (df
# Use last 30 days of data to compare prices
.loc[lambda d: d.date>=(datetime.now()-timedelta(days=30))]
.sort_values('date')
# Get percentage change
.assign(change=lambda d: d
.groupby(['store','sku'], as_index=False)
.price.transform(lambda d: (d-d.shift()).iloc[-1])
)
.groupby(['store','sku'], as_index=False)
.agg({'price':'last', 'change':'mean', 'date':'last'})
.rename({'price':'last_price', 'date':'last_date'}, axis=1)
.dropna()
.loc[lambda d: d.last_date==d.last_date.max()]
.loc[lambda d: d.change.abs().sort_values(ascending=False).index]
)
top_changes_abs.head(3)| store | sku | last_price | change | last_date | |
|---|---|---|---|---|---|
| 4669 | plaza_vea | 11339782 | 122.50 | -31.5 | 2025-03-27 |
| 27 | plaza_vea | 258 | 41.99 | -18.0 | 2025-03-27 |
| 603 | plaza_vea | 2946 | 37.90 | 16.0 | 2025-03-27 |
Code
top_changes_abs.head(10).pipe(plot_changes, 'Top changes')Code
(top_changes_abs
.sort_values('change')
.head(10)
.pipe(plot_changes, 'Top drops')
)Code
(top_changes_abs
.sort_values('change')
.tail(10)
.pipe(plot_changes, 'Top increases')
)Search specific products
Code
(df
.loc[df.name.isin(names)]
.pipe(alt.Chart)
.mark_line(point=True)
.encode(x='date', y='price', color='name', tooltip=['name','price'])
.properties(width=650, title='Pollo')
.interactive()
.configure_legend(orient='top', columns=3)
)Code
(df
.loc[df.name.isin(names)]
.pipe(alt.Chart)
.mark_line(point=True)
.encode(x='date', y='price', color='name', tooltip=['name','price'])
.properties(width=650, title='Palta')
.interactive()
.configure_legend(orient='top', columns=3)
)Code
(df
.loc[df.name.isin(names)]
.pipe(alt.Chart)
.mark_line(point=True)
.encode(x='date', y='price', color='name', tooltip=['name','price'])
.properties(width=650, title='Aceite')
.interactive()
.configure_legend(orient='top', columns=3)
)Code
(df
.loc[df.name.isin(names)]
.pipe(alt.Chart)
.mark_line(point=True)
.encode(x='date', y='price', color='name', tooltip=['name','price'])
.properties(width=650, title='Aceite')
.interactive()
.configure_legend(orient='top', columns=3)
)Code
(df
.loc[df.name.isin(names)]
.pipe(alt.Chart)
.mark_line(point=True)
.encode(x='date', y='price', color='name', tooltip=['name','price'])
.properties(width=650, title='Aceite')
.interactive()
.configure_legend(orient='top', columns=3)
)